package com.yanqu.road.server.manger.sacrifice;

import com.yanqu.road.logic.bussiness.player.UserSacrificeBussiness;
import com.yanqu.road.logic.rank.RankListModel;
import com.yanqu.road.logic.thread.ThreadTaskManger;
import com.yanqu.road.logic.pb.RankPb;
import com.yanqu.road.logic.config.GameConfig;
import com.yanqu.road.entity.config.sacrifice.SacrificeBoss;
import com.yanqu.road.entity.config.sacrifice.SacrificeDetail;
import com.yanqu.road.entity.enums.*;
import com.yanqu.road.entity.player.UserInfo;
import com.yanqu.road.entity.player.UserPatrons;
import com.yanqu.road.entity.rank.UserRank;
import com.yanqu.road.entity.sacrifice.SacrificeAttackBossResult;
import com.yanqu.road.entity.sacrifice.UserSacrifice;
import com.yanqu.road.entity.sacrifice.UserSacrificePatronsData;
import com.yanqu.road.pb.rank.RankProto;
import com.yanqu.road.server.TempMgr;
import com.yanqu.road.server.GameServer;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.patrons.PatronsModule;
import com.yanqu.road.server.gameplayer.module.player.CurrencyModule;
import com.yanqu.road.server.gameplayer.module.sacrifice.SacrificeModule;
import com.yanqu.road.server.logic.mail.MailManager;
import com.yanqu.road.server.manger.MultipleLanguageMgr;
import com.yanqu.road.server.manger.config.SacrificeConfigMgr;
import com.yanqu.road.server.manger.config.SystemOpenMgr;
import com.yanqu.road.server.manger.log.LogMgr;
import com.yanqu.road.server.manger.player.UserMgr;
import com.yanqu.road.server.manger.player.GamePlayerMgr;
import com.yanqu.road.server.manger.sacrifice.task.RecordUserInfoTask;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.LocalDateTimeHelper;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.executor.GeneralTask;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;

import java.math.BigInteger;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class SacrificeMgr extends TempMgr {

    private static final int ABILITY_RECORD_MARK_ACTIVE = 1;           //活跃记录实力位

    private static final int ABILITY_RECORD_MARK_IN_TIME = 2;          //活动期间内记录实力位

    private static final int ABILITY_RECORD_MARK_JOIN = 3;             //参与记录实力位

    private static final Object lockObj = new Object();

    private static HashSet<Long> inSceneUserIdSet;

    private static long lastResetTime;

    private static LinkedList<SacrificeDetail> sacrificeDetailList;

    private static Map<Integer, RankListModel> rankListModelMap;

    private static ThreadTaskManger threadTaskManger;

    private static AtomicInteger taskId = new AtomicInteger(1);

    @Override
    public boolean init() throws Exception {
        return reload();
    }

    @Override
    public boolean reloadConfig() throws Exception {
        return true;
    }

    @Override
    public boolean stop() {
        return false;
    }

    @Override
    public boolean save() {
        for(SacrificeDetail sacrificeDetail : sacrificeDetailList){
            if(sacrificeDetail.isInsertOption()){
                UserSacrificeBussiness.addSacrificeDetail(sacrificeDetail);
            }else if(sacrificeDetail.isUpdateOption()){
                UserSacrificeBussiness.updateSacrificeDetail(sacrificeDetail);
            }
        }
        for(RankListModel rankListModel : rankListModelMap.values()){
            rankListModel.saveSacrificeDamageRankList();
        }
        return true;
    }

    @Override
    public boolean reloadData() {
        inSceneUserIdSet = new HashSet<>();
        if(null == threadTaskManger) {
            threadTaskManger = new ThreadTaskManger(4, "sacrificeTaskManger");
        }
        taskId = new AtomicInteger(1);
        rankListModelMap = new ConcurrentHashMap<>();
        long zeroTime = LocalDateTimeHelper.getZeroTimeTimeStamp() / 1000;
        sacrificeDetailList = UserSacrificeBussiness.getSacrificeDetailList(zeroTime, GameConfig.SACRIFICE_DETAIL_MAX_COUNT);
        checkSacrificeDetail();
        int dateMark = (int) getNowSacrificeDetailDateMark();
        LinkedList<UserRank> userRankList = UserSacrificeBussiness.getSacrificeDamageRankList(dateMark);
        RankListModel rankListModel = new RankListModel(eBigRankType.Normal.getValue(), dateMark);
        rankListModel.setRankNum(SacrificeConfigMgr.getSacrificeDamageMaxRank());
        rankListModel.setRankList(userRankList);
        rankListModelMap.put(dateMark, rankListModel);
        lastResetTime = zeroTime;
        return true;
    }

    //每日重置
    public static void resetOneDay(){
        long zeroTime = LocalDateTimeHelper.getZeroTimeTimeStamp() / 1000;
        if(lastResetTime < zeroTime){
            synchronized (inSceneUserIdSet) {
                inSceneUserIdSet.clear();
            }
            checkSacrificeDetail();
            lastResetTime = zeroTime;
        }
    }

    public static void addTask(GeneralTask task) {
        threadTaskManger.addTask(taskId.getAndIncrement(), task);
    }

    public static void sendRankReward(){
        long nowDateMark = getNowSacrificeDetailDateMark();
        for(SacrificeDetail detail : sacrificeDetailList){
            boolean needSend = false;
            if(!detail.isSendReward()){//还未发放奖励
                if(detail.getDateMark() < nowDateMark){
                    needSend = true;
                }else if(detail.getDateMark() == nowDateMark){
                    needSend = SacrificeConfigMgr.isTodaySendSacrificeRewardTime(detail);
                }
                if(needSend) {
                    String dateString = DateHelper.getPyyyyMdDateString(detail.getDateMark());
                    int dateMark = (int) detail.getDateMark();
                    RankListModel rankListModel = rankListModelMap.get(dateMark);
                    if (null == rankListModel) {
                        rankListModel = new RankListModel(eBigRankType.Normal.getValue(), dateMark);
                        rankListModel.setRankNum(SacrificeConfigMgr.getSacrificeDamageMaxRank());
                        LinkedList<UserRank> userRankList = UserSacrificeBussiness.getSacrificeDamageRankList(dateMark);
                        rankListModel.setRankList(userRankList);
                        rankListModelMap.put(dateMark, rankListModel);
                    }
                    boolean isAllSend = true;
                    for(UserRank userRank : rankListModel.getRankList()){
                        if(!"1".equals(userRank.getParam())){
                            int rank = rankListModel.getMyRank(userRank.getUserId());
                            Property reward = SacrificeConfigMgr.getSacrificeDamageReward(rank);
                            if(null != reward){
                                userRank.setParam("1");
                                if(!UserSacrificeBussiness.updateSacrificeDamageGetReward(userRank)){
                                    isAllSend = false;
                                    continue;
                                }
                                String language = UserMgr.getLanguage(userRank.getUserId());
                                String mailTitle = MultipleLanguageMgr.getContent(MailManager.SACRIFICE_DAMAGE_RANK_REWARD_TITLE, language);
                                String mailContent = StringUtils.format(MultipleLanguageMgr.getContent(MailManager.SACRIFICE_DAMAGE_RANK_REWARD_CONTENT, language),
                                        dateString, rank);
                                String rewardStr = PropertyHelper.parsePropertyToString(reward);
                                MailManager.sendMail(userRank.getUserId(), eMailType.SYSTEM.getValue(), rewardStr, mailContent, mailTitle);
                                LogMgr.addLogSacrificeDamageRankReward(userRank.getUserId(), dateMark, rank, userRank.getValue(), rewardStr);
                            }
                        }
                    }
                    if(isAllSend) {
                        detail.setSendReward(true);
                    }
                }
            }
        }
    }

    public static void addUserInSacrificeScene(long userId){
        synchronized (inSceneUserIdSet) {
            inSceneUserIdSet.add(userId);
        }
    }

    public static void removeUserInSacrificeScene(long userId){
        synchronized (inSceneUserIdSet) {
            inSceneUserIdSet.remove(userId);
        }
    }

    public static HashSet<Long> getInSceneUserIdSet() {
        HashSet<Long> result;
        synchronized (inSceneUserIdSet){
            result = new HashSet<>(inSceneUserIdSet);
        }
        return result;
    }

    public static SacrificeAttackBossResult sacrificeAttackBoss(GamePlayer player, int patronsId){
        checkSacrificeDetail();
        SacrificeAttackBossResult result = new SacrificeAttackBossResult();
        if(!SystemOpenMgr.systemOpen(player, eSystemId.Sacrifice.getValue())){
            result.setRet(GameErrorCode.E_SYSTEM_NO_OPEN);
            return result;
        }
        if(!SacrificeConfigMgr.isSacrificeSystemOpenTime()){
            result.setRet(GameErrorCode.E_SYSTEM_NO_OPEN);
            return result;
        }
        if(!SacrificeConfigMgr.isInSacrificeAttackBossTime()){
            result.setRet(GameErrorCode.E_SACRIFICE_NO_ATTACK_BOSS_TIME);
            return result;
        }
        SacrificeDetail sacrificeDetail = getNowSacrificeDetail();
        if(sacrificeDetail.getCurrentBlood().compareTo(BigInteger.ZERO) <= 0){
            result.setRet(GameErrorCode.E_SACRIFICE_BOSS_IS_DEAD);
            result.setKillBossUserId(sacrificeDetail.getKillBossUserId());
            result.setKillBossNickName(sacrificeDetail.getKillBossNickName());
            result.setKillBossDecoration(sacrificeDetail.getKillBossDecoration());
            return result;
        }
        UserPatrons userPatrons = player.getModule(PatronsModule.class).getUserPatrons(patronsId);
        if(null == userPatrons){
            result.setRet(GameErrorCode.E_PATRONS_NO_FOUND);
            return result;
        }
        SacrificeModule sacrificeModule = player.getModule(SacrificeModule.class);
        UserSacrificePatronsData userSacrificePatronsData = sacrificeModule.getUserSacrificePatronsData(patronsId);
        if(null == userSacrificePatronsData){
            sacrificeModule.notifyAddPatrons(userPatrons);
            userSacrificePatronsData = sacrificeModule.getUserSacrificePatronsData(patronsId);
            if(null == userSacrificePatronsData) {
                result.setRet(GameErrorCode.E_PATRONS_NO_FOUND);
                return result;
            }
        }
        if(userSacrificePatronsData.getBattleTimes() >= GameConfig.SACRIFICE_PATRONS_DEFAULT_ATTACK_TIMES){
            sacrificeModule.syncUserSacrificePatronsData(userSacrificePatronsData);   //没次数了，同步一下数据
            result.setRet(GameErrorCode.E_SACRIFICE_PATRONS_ATTACK_TIMES_OVER);
            return result;
        }
        userSacrificePatronsData.setBattleTimes(userSacrificePatronsData.getBattleTimes() + 1);
        BigInteger ability = player.getModule(PatronsModule.class).getTotalPatronsAbility();
        BigInteger damage = SacrificeConfigMgr.getPatronsDamage(player, userPatrons);
        UserSacrifice userSacrifice = player.getModule(SacrificeModule.class).getUserSacrifice();
        boolean isKill = false;
        BigInteger oldBlood;
        synchronized (lockObj) {
            oldBlood = sacrificeDetail.getCurrentBlood();
            if(!isAbilityRecord(userSacrifice, ABILITY_RECORD_MARK_JOIN)) {
                addAbilityRecord(userSacrifice, ABILITY_RECORD_MARK_JOIN, sacrificeDetail, ability);
            }
            if (damage.compareTo(sacrificeDetail.getCurrentBlood()) >= 0){//击杀
                isKill = true;
                sacrificeDetail.setCurrentBlood(BigInteger.ZERO);
            }else {
                sacrificeDetail.setCurrentBlood(sacrificeDetail.getCurrentBlood().subtract(damage));
            }
        }
        if(isKill){//死了记录boss剩余血量
            userSacrifice.setTodayDamage(userSacrifice.getTodayDamage().add(oldBlood));
        }else {//没死记录全伤害
            userSacrifice.setTodayDamage(userSacrifice.getTodayDamage().add(damage));
        }
        Property reward = SacrificeConfigMgr.getSacrificeAttackBossReward(player, userPatrons);
        if(isKill){
            sacrificeDetail.setKillTime(System.currentTimeMillis() / 1000);
            sacrificeDetail.setKillBossUserId(player.getUserId());
            sacrificeDetail.setKillBossNickName(player.getUserInfo().getNickName());
            sacrificeDetail.setKillBossDecoration(UserMgr.getDecoration(player.getUserId()));
            result.setKillBossUserId(player.getUserId());
            result.setKillBossNickName(player.getUserInfo().getNickName());
            result.setKillBossDecoration(UserMgr.getDecoration(player.getUserId()));
            reward.addProperty(PropertyHelper.parseStringToProperty(GameConfig.SACRIFICE_BOSS_KILL_REWARD));  //击杀奖励
            userSacrifice.setTotalKillBossTimes(userSacrifice.getTotalKillBossTimes() + 1);
            player.notifyListener(eGamePlayerEventType.TotalKillBossTimes.getValue(), userSacrifice);
        }
        player.getModule(CurrencyModule.class).addCurrency(reward, eLogMoneyType.Sacrifice, eLogMoneyType.SacrificeAttack);
        result.setTotalBlood(sacrificeDetail.getTotalBlood());
        result.setCurrentBlood(sacrificeDetail.getCurrentBlood());
        result.setDamage(damage);
        String rewardStr = PropertyHelper.parsePropertyToString(reward);
        result.setReward(rewardStr);
        changeSacrificeDamageRank(player.getUserId(), userSacrifice.getTodayDamage());
        player.getModule(SacrificeModule.class).syncUserSacrifice();
        LogMgr.addLogSacrificeAttack(player.getUserId(), sacrificeDetail.getDateMark(), sacrificeDetail.getBossLevel(), oldBlood, sacrificeDetail.getCurrentBlood(),
                patronsId, damage, rewardStr);
        if(isKill){
            LogMgr.addLogSacrificeKill(player.getUserId(), sacrificeDetail.getDateMark(), sacrificeDetail.getBossLevel(), rewardStr);
        }
        return result;
    }

    public static RankListModel getSacrificeDamageRankList(){
        int dateMark = (int) getNowSacrificeDetailDateMark();
        return rankListModelMap.get(dateMark);
    }

    private static void notifyTopOneDamage(UserRank topOneData){
        if(!SacrificeConfigMgr.isInSacrificeAttackBossTime()){
            return;
        }
        RankProto.RankTempMsg.Builder msg = RankPb.parseRankTempMsg(topOneData);
        if(null == msg){
            return;
        }
        for(long userId : getInSceneUserIdSet()){
            GamePlayer player = GamePlayerMgr.getOnlinePlayer(userId);
            if(null != player){
                player.sendPacket(Protocol.U_SACRIFICE_DAMAGE_TOP_ONE_SYNC, msg);
            }
        }
    }

    private static void changeSacrificeDamageRank(long userId, BigInteger value) {
        if(!SacrificeConfigMgr.isInSacrificeAttackBossTime()){
            return;
        }
        int dateMark = (int)getNowSacrificeDetailDateMark();
        RankListModel rankListModel = rankListModelMap.get(dateMark);
        if (null == rankListModel) {
            return;
        }
        UserInfo userInfo = UserMgr.getUserInfo(userId);
        if(null == userInfo){
            return;
        }
        //管理员账号不参与排行
        if (userInfo.isAdmin()) {
            return;
        }
        //大于0的才参与排行榜
        if (value.compareTo(BigInteger.ZERO) > 0) {
            long oldUserId = 0;
            long newUserId = 0;
            BigInteger oldValue = BigInteger.ZERO;
            UserRank oldFirst = rankListModel.getFirstRank();
            if (null != oldFirst) {
                oldUserId = oldFirst.getUserId();
                oldValue = oldFirst.getValue();
            }
            rankListModel.rankChange(userId, UserMgr.parseUserBaseInfo(userInfo, GameServer.getInstance().getServerId()), value, "");
            UserRank newFirst = rankListModel.getFirstRank();
            if (null != newFirst) {
                newUserId = newFirst.getUserId();
                boolean needNotify = false;
                if(0 != newUserId){
                    if(oldUserId == newUserId){
                        if(!Objects.equals(oldValue, newFirst.getValue())){
                            needNotify = true;
                        }
                    }else {
                        needNotify = true;
                    }
                }
                if (needNotify) {
                    SacrificeMgr.notifyTopOneDamage(newFirst);
                }
            }
        }
    }

    public static void checkSacrificeDetail(){
        long zeroTime = LocalDateTimeHelper.getZeroTimeTimeStamp() / 1000;
        SacrificeDetail sacrificeDetail = null;
        SacrificeDetail lastSacrificeDetail = null;
        synchronized (lockObj) {
            if(sacrificeDetailList.size() > 0){
                sacrificeDetail = sacrificeDetailList.getFirst();
            }
            if (null == sacrificeDetail) {
                sacrificeDetail = initSacrificeDetail(zeroTime);
            }else {
                if(sacrificeDetail.getDateMark() != zeroTime){
                    lastSacrificeDetail = sacrificeDetail;
                    sacrificeDetail = initSacrificeDetail(zeroTime);
                }else {
                    if(sacrificeDetailList.size() > 1){
                        lastSacrificeDetail = sacrificeDetailList.get(1);
                    }
                }
                //19点前关服，过0点后开服，造成lastSacrificeDetail未初始化，今日bossLevel初始化为1级，现修改兼容可关服24H
                if(lastSacrificeDetail != null && lastSacrificeDetail.getBossLevel() == 0 && sacrificeDetailList.size() > 2){
                    lastSacrificeDetail = sacrificeDetailList.get(2);
                }
            }
            long nowTime = System.currentTimeMillis() / 1000;
            if(BigInteger.ZERO.compareTo(sacrificeDetail.getTotalBlood()) == 0 && nowTime - sacrificeDetail.getDateMark() >= GameConfig.SACRIFICE_INIT_BOSS_TIME){
                //未初始化，且在时间内
                SacrificeBoss sacrificeBoss = SacrificeConfigMgr.getSacrificeBoss(lastSacrificeDetail, sacrificeDetail);
                sacrificeDetail.setBossLevel(sacrificeBoss.getLevel());
                sacrificeDetail.setTotalBlood(sacrificeBoss.getBlood());
                sacrificeDetail.setCurrentBlood(sacrificeBoss.getBlood());
            }
        }
    }

    private static SacrificeDetail initSacrificeDetail(long zeroTime){
        SacrificeDetail tempDetail = new SacrificeDetail();
        tempDetail.setDateMark(zeroTime);
        tempDetail.setInsertOption();
        RankListModel rankListModel = new RankListModel(eBigRankType.Normal.getValue(), (int) zeroTime);
        rankListModel.setRankNum(SacrificeConfigMgr.getSacrificeDamageMaxRank());
        rankListModelMap.put((int) zeroTime, rankListModel);
        sacrificeDetailList.addFirst(tempDetail);
        return tempDetail;
    }

    public static void taskRecordPlayerInfo(GamePlayer player){
        addTask(new RecordUserInfoTask(player));
    }

    public static void recordPlayerInfo(GamePlayer player){
        UserSacrifice userSacrifice = player.getModule(SacrificeModule.class).getUserSacrifice();
        BigInteger ability = player.getModule(PatronsModule.class).getTotalPatronsAbility();
        SacrificeDetail sacrificeDetail = getNowSacrificeDetail();
        if(!SystemOpenMgr.systemOpen(player, eSystemId.Sacrifice.getValue())){//未系统解锁
            return;
        }
        if(!isAbilityRecord(userSacrifice, ABILITY_RECORD_MARK_ACTIVE)) {
            synchronized (lockObj) {
                addAbilityRecord(userSacrifice, ABILITY_RECORD_MARK_ACTIVE, sacrificeDetail, ability);
            }
        }
        if(SacrificeConfigMgr.isInSacrificeAttackBossTime()){
            if(!isAbilityRecord(userSacrifice, ABILITY_RECORD_MARK_IN_TIME)) {
                synchronized (lockObj) {
                    addAbilityRecord(userSacrifice, ABILITY_RECORD_MARK_IN_TIME, sacrificeDetail, ability);
                }
            }
        }
    }

    public static SacrificeDetail getNowSacrificeDetail() {
        checkSacrificeDetail();
        return sacrificeDetailList.getFirst();
    }

    public static long getNowSacrificeDetailDateMark() {
        return getNowSacrificeDetail().getDateMark();
    }

    private static boolean isAbilityRecord(UserSacrifice userSacrifice, int abilityRecordMark){
        if(null == userSacrifice){
            return true;
        }
        return (userSacrifice.getAbilityRecordMark() & (long) Math.pow(2, abilityRecordMark)) != 0;
    }

    private static void addAbilityRecord(UserSacrifice userSacrifice, int abilityRecordMark, SacrificeDetail sacrificeDetail, BigInteger ability){
        userSacrifice.setAbilityRecordMark(userSacrifice.getAbilityRecordMark() | (long) Math.pow(2, abilityRecordMark));
        if(ABILITY_RECORD_MARK_ACTIVE == abilityRecordMark) {
            sacrificeDetail.setActiveUserCount(sacrificeDetail.getActiveUserCount() + 1);
            sacrificeDetail.setActiveAbility(sacrificeDetail.getActiveAbility().add(ability));
        }else if(ABILITY_RECORD_MARK_IN_TIME == abilityRecordMark){
            sacrificeDetail.setInTimeUserCount(sacrificeDetail.getInTimeUserCount() + 1);
            sacrificeDetail.setInTimeAbility(sacrificeDetail.getInTimeAbility().add(ability));
        }else if(ABILITY_RECORD_MARK_JOIN == abilityRecordMark){
            sacrificeDetail.setJoinUserCount(sacrificeDetail.getJoinUserCount() + 1);
            sacrificeDetail.setJoinAbility(sacrificeDetail.getJoinAbility().add(ability));
        }
    }
}
